﻿using DotNetCommon.Extensions;
using System;
using System.Collections;
using System.Collections.Generic;
using static System.Runtime.InteropServices.JavaScript.JSType;

namespace DotNetCommon.Validate;

/// <summary>
/// 校验基础扩展:
/// <list type="bullet">
/// <item>判断是否为null</item>
/// <item>判断是否相等 class.Equals</item>
/// <item>判断大小 IComparable</item>
/// <item>判断是否可转换 object.To&lt;int>()</item>
/// </list>
/// </summary>
public static class ValidateExtensions
{
    #region MustNotNull
    /// <summary>
    /// 不能为空
    /// </summary>
    public static IValidateContext<T> MustNotNull<T>(this IValidateContext<T> ctx, string errorMessage = null)
    {
        if (ctx.StopValidate) return ctx;
        MustNotNullInternal(ctx, errorMessage);
        return ctx;
    }

    internal static void MustNotNullInternal(IValidateContext ctx, string errorMessage = null)
    {
        if (ctx.ModelValue == null) ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 不能为 null !");
    }

    /// <summary>
    /// 必须为空
    /// </summary>
    public static IValidateContext<T> MustBeNull<T>(this IValidateContext<T> ctx, string errorMessage = null)
    {
        if (ctx.StopValidate) return ctx;
        MustBeNullInternal(ctx, errorMessage);
        return ctx;
    }

    internal static void MustBeNullInternal(IValidateContext ctx, string errorMessage = null)
    {
        if (ctx.ModelValue != null) ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 必须为 null !");
    }
    #endregion

    #region MustCanConvertTo
    /// <summary>
    /// 必须可以转换到类型T(如: string.To&lt;int>() 可以成功)
    /// </summary>
    public static IValidateContext<T> MustCanConvertTo<T>(this IValidateContext ctx, string errorMessage = null)
    {
        if (ctx.StopValidate) return new ValidateContext<T>(default, null, ctx, true);
        return MustCanConvertToInternal<T>(ctx, errorMessage);
    }

    internal static IValidateContext<T> MustCanConvertToInternal<T>(IValidateContext ctx, string errorMessage = null)
    {
        try
        {
            var newModel = ctx.ModelValue.To<T>();
            return new ValidateContext<T>(newModel, ctx);
        }
        catch (Exception ex)
        {
            ctx.AddError(ctx.ModelName, errorMessage ?? $"无法将 '{ctx.ModelPath}' 转换成 {typeof(T).GetClassFullName()} ! 报错: {ex.Message}");
            var newCtx = new ValidateContext<T>(default, null, ctx, true);
            return newCtx;
        }
    }
    #endregion

    #region MustBe 使用 Object.Equals
    /// <summary>
    /// 必须等于value
    /// </summary>
    public static IValidateContext<T> MustBe<T>(this IValidateContext<T> ctx, T value, string errorMessage = null)
    {
        MustBeInternal(ctx, value, errorMessage);
        return ctx;
    }
    internal static void MustBeInternal(IValidateContext ctx, object value, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (ctx.ModelValue == null && value == null) return;
        if (ctx.ModelValue == null || value == null)
        {
            ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 必须等于 {value?.ToString() ?? "null"} !");
            return;
        }
        var t = ctx.ModelValue.GetType();
        if (!ctx.ModelValue.Equals(value.To(t)))
        {
            ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 必须等于 {value?.ToString() ?? "null"} !");
        }
    }
    /// <summary>
    /// 必须不等于value
    /// </summary>
    public static IValidateContext<T> MustNotBe<T>(this IValidateContext<T> ctx, T value, string errorMessage = null)
    {
        MustNotBeInternal(ctx, value, errorMessage);
        return ctx;
    }
    internal static void MustNotBeInternal<T>(IValidateContext ctx, T value, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (ctx.ModelValue == null && value == null)
        {
            ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 不能等于 {value?.ToString() ?? "null"} !");
            return;
        }
        if (ctx.ModelValue == null || value == null) return;
        var t = ctx.ModelValue.GetType();
        if (ctx.ModelValue.Equals(value.To(t)))
        {
            ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 不能等于 {value?.ToString() ?? "null"} !");
        }
    }
    #endregion

    #region MustIn 不使用 IEquatable<T>
    #region MustIn
    /// <summary>
    /// 必须在集合中
    /// </summary>
    /// <remarks>
    /// 注意: 如果集合为null, 则直接通过
    /// </remarks>
    public static IValidateContext<T> MustIn<T>(this IValidateContext<T> ctx, IEnumerable<T> list, string errorMessage = null)
    {
        MustInInternal(ctx, list, errorMessage);
        return ctx;
    }
    internal static void MustInInternal(IValidateContext ctx, IEnumerable list, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (list == null) return;
        if (ctx.ModelValue is null)
        {
            if (EnumerableExtensions.Any(list, i => i == null)) return;
            ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 必须是 {list.ToJson()} 之一!");
            return;
        }
        var t = ctx.ModelValue.GetType();
        if (EnumerableExtensions.Any(list, obj => obj != null && ctx.ModelValue.Equals(obj.To(t)))) return;
        ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 必须是 {list.ToJson()} 之一!");
    }
    #endregion

    #region MustNotIn
    /// <summary>
    /// 不能在集合中
    /// </summary>
    public static IValidateContext<T> MustNotIn<T>(this IValidateContext<T> ctx, IEnumerable<T> list, string errorMessage = null)
    {
        MustNotInInternal(ctx, list, errorMessage);
        return ctx;
    }
    internal static void MustNotInInternal(IValidateContext ctx, IEnumerable list, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (list == null) return;
        var flag = false;
        foreach (var item in list)
        {
            flag = true;
            break;
        }
        if (!flag) return;

        if (ctx.ModelValue is null)
        {
            if (EnumerableExtensions.Any(list, i => i == null))
            {
                ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 不能是 {list.ToJson()} 之一!");
                return;
            }
            return;
        }
        var t = ctx.ModelValue.GetType();
        if (EnumerableExtensions.Any(list, i => i != null && ctx.ModelValue.Equals(i.To(t))))
        {
            ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 不能是 {list.ToJson()} 之一!");
        }
    }
    #endregion
    #endregion

    #region MustGreater/LessThan
    #region MustLessThan
    /// <summary>
    /// 值必须小于value
    /// </summary>
    /// <remarks>
    /// 注意: value值不能为null, 否则抛异常
    /// </remarks>
    public static IValidateContext<T> MustLessThan<T>(this IValidateContext<T> ctx, T value, string errorMessage = null)
    {
        MustLessThanInternal(ctx, value, errorMessage);
        return ctx;
    }
    internal static void MustLessThanInternal<T>(IValidateContext ctx, T value, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        //这里无法跳过
        AssertUtil.NotNull(value);
        if (ctx.ModelValue is null || ctx.ModelValue is not IComparable comparable)
        {
            ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 必须小于 {value} !");
            return;
        }
        //防止数据类型不一致报错(Attribute 传递进来的, 如: [VMustGreaterThan(0)] prop double Score)
        var t = ctx.ModelValue.GetType();
        object v = value;
        if (!t.IsAssignableFrom(value.GetType()))
        {
            v = value.To(t, null);
        }
        if (comparable.CompareTo(v) >= 0)
        {
            ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 必须小于 {value} !");
        }
    }
    #endregion

    #region MustLessThanOrEuqalTo
    /// <summary>
    /// 值必须小于或等于value
    /// </summary>
    /// <remarks>
    /// 注意: value值不能为null, 否则抛异常
    /// </remarks>
    public static IValidateContext<T> MustLessThanOrEuqalTo<T>(this IValidateContext<T> ctx, T value, string errorMessage = null)
    {
        MustLessThanOrEuqalToInternal(ctx, value, errorMessage);
        return ctx;
    }
    internal static void MustLessThanOrEuqalToInternal<T>(IValidateContext ctx, T value, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (ctx.ModelValue == null && value == null) return;
        //这里无法跳过
        AssertUtil.NotNull(value);
        if (ctx.ModelValue is null || ctx.ModelValue is not IComparable comparable)
        {
            ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 必须小于等于 {value} !");
            return;
        }
        //防止数据类型不一致报错(Attribute 传递进来的, 如: [VMustGreaterThan(0)] prop double Score)
        var t = ctx.ModelValue.GetType();
        object v = value;
        if (!t.IsAssignableFrom(value.GetType()))
        {
            v = value.To(t, null);
        }
        if (comparable.CompareTo(v) > 0)
        {
            ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 必须小于等于 {value} !");
        }
    }
    #endregion

    #region MustGreaterThan
    /// <summary>
    /// 值必须大于某个值
    /// </summary>
    /// <remarks>
    /// 注意: value值不能为null, 否则抛异常
    /// </remarks>
    public static IValidateContext<T> MustGreaterThan<T>(this IValidateContext<T> ctx, T value, string errorMessage = null)
    {
        MustGreaterThanInternal(ctx, value, errorMessage);
        return ctx;
    }
    internal static void MustGreaterThanInternal<T>(IValidateContext ctx, T value, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        //这里无法跳过
        AssertUtil.NotNull(value);
        if (ctx.ModelValue is null || ctx.ModelValue is not IComparable comparable)
        {
            ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 必须大于 {value} !");
            return;
        }
        //防止数据类型不一致报错(Attribute 传递进来的, 如: [VMustGreaterThan(0)] prop double Score)
        var t = ctx.ModelValue.GetType();
        object v = value;
        if (!t.IsAssignableFrom(value.GetType()))
        {
            v = value.To(t, null);
        }
        if (comparable.CompareTo(v) <= 0)
        {
            ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 必须大于 {value} !");
        }
    }
    #endregion

    #region MustGreaterThanOrEuqalTo
    /// <summary>
    /// 值必须大于等于某个值
    /// </summary>
    /// <remarks>
    /// 注意: value值不能为null, 否则抛异常
    /// </remarks>
    public static IValidateContext<T> MustGreaterThanOrEuqalTo<T>(this IValidateContext<T> ctx, T value, string errorMessage = null)
    {
        MustGreaterThanOrEuqalToInternal(ctx, value, errorMessage);
        return ctx;
    }
    internal static void MustGreaterThanOrEuqalToInternal<T>(IValidateContext ctx, T value, string errorMessage = null)
    {
        if (ctx.StopValidate) return;
        if (ctx.ModelValue == null && value == null) return;
        //这里无法跳过
        AssertUtil.NotNull(value);
        if (ctx.ModelValue is null || ctx.ModelValue is not IComparable comparable)
        {
            ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 必须大于等于 {value} !");
            return;
        }
        //防止数据类型不一致报错(Attribute 传递进来的, 如: [VMustGreaterThan(0)] prop double Score)
        var t = ctx.ModelValue.GetType();
        object v = value;
        if (!t.IsAssignableFrom(value.GetType()))
        {
            v = value.To(t, null);
        }
        if (comparable.CompareTo(v) < 0)
        {
            ctx.AddError(ctx.ModelPath, errorMessage ?? $"'{ctx.ModelPath}' 必须大于等于 {value} !");
        }
    }
    #endregion
    #endregion
}
